<?php
/**
 +------------------------------------------------------------------------------
 * 基于角色和职位的数据库方式验证类
 +------------------------------------------------------------------------------
 * @category   ORG
 * @package  ORG
 * @subpackage  Util
 * @author    liufei
 +------------------------------------------------------------------------------
 */

// 配置文件增加设置
// USER_AUTH_ON 是否需要认证
// USER_AUTH_TYPE 认证类型 1 登录认证 2 实时认证
// USER_AUTH_KEY 用户认证SESSION标记
// ADMIN_AUTH_KEY 管理员认证SESSION标记
// USER_AUTH_GATEWAY 认证网关
// NOT_AUTH_MODULE 无需认证模块
// NOT_AUTH_ACTION 无需认证的操作
// RBAC_ACCESS_TABLE 总权限表
// RBAC_POSITION_TABLE 职位权限表
// RBAC_ROLE_TABLE 角色权限表
// RBAC_NODE_TABLE 操作节点表

/*
+------------------------------------------------------------------------------
CREATE TABLE `fee_admin_node` (
`nodeid` smallint(5) unsigned NOT NULL AUTO_INCREMENT COMMENT '节点ID',
`reid` smallint(5) unsigned NOT NULL DEFAULT '0' COMMENT '上级分类ID',
`typeid` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '0分组 1模块 2动作',
`name` varchar(50) NOT NULL DEFAULT '' COMMENT '节点名称',
`code` char(30) NOT NULL DEFAULT '' COMMENT '权限标示',
`description` varchar(255) NOT NULL DEFAULT '' COMMENT '节点描述',
`sort` smallint(6) NOT NULL DEFAULT '10' COMMENT '排序',
`addtime` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '添加时间',
`is_check` tinyint(1) unsigned NOT NULL DEFAULT '1' COMMENT '状态 0禁用 1正常',
PRIMARY KEY (`nodeid`)
) ENGINE=MyISAM AUTO_INCREMENT=12 DEFAULT CHARSET=utf8 COMMENT='系统全部操作节点，基本权限表';


CREATE TABLE `fee_admin_role` (
`roleid` smallint(5) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(20) NOT NULL DEFAULT '' COMMENT '角色名称',
`description` varchar(255) NOT NULL DEFAULT '' COMMENT '角色描述',
`nodeids` varchar(10000) NOT NULL DEFAULT '' COMMENT '节点ID集合',
`sort` smallint(5) unsigned NOT NULL DEFAULT '10' COMMENT '排序',
`is_check` tinyint(1) unsigned NOT NULL DEFAULT '1' COMMENT '状态 0禁用 1正常',
PRIMARY KEY (`roleid`)
) ENGINE=MyISAM AUTO_INCREMENT=12 DEFAULT CHARSET=utf8 COMMENT='角色表，基本权限的集合。无上级与下级之分';

CREATE TABLE `fee_admin_user` (
`uid` mediumint(8) unsigned NOT NULL DEFAULT '0' COMMENT '管理员UID 关联用户表UID',
`username` varchar(30) NOT NULL DEFAULT '' COMMENT '用户名',
`realname` varchar(10) NOT NULL DEFAULT '' COMMENT '真实姓名',
`password` varchar(100) NOT NULL DEFAULT '',
`lasttime` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '上次访问时间',
`lastip` char(15) NOT NULL DEFAULT '' COMMENT '上次访问IP',
`logincount` mediumint(8) unsigned NOT NULL DEFAULT '0' COMMENT '登录次数',
`safecode` varchar(8) NOT NULL DEFAULT '' COMMENT '登录安全码',
`email` varchar(50) NOT NULL DEFAULT '' COMMENT '常用邮箱',
`remark` varchar(255) NOT NULL DEFAULT '' COMMENT '备注',
`addtime` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '添加时间',
`lastupdatetime` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '最后一次更新时间',
`nodeids` varchar(10000) NOT NULL DEFAULT '' COMMENT '操作节点ID集合',
`roleids` varchar(255) NOT NULL DEFAULT '' COMMENT '角色ID集合',
`is_check` tinyint(1) unsigned NOT NULL DEFAULT '1' COMMENT '账号是否禁用 0禁用 1正常',
UNIQUE KEY `uid` (`uid`)
) ENGINE=MyISAM AUTO_INCREMENT=7 DEFAULT CHARSET=utf8 COMMENT='后台用户表';
*/

class RBAC {

	protected $tables; // 待操作的用户表
	protected $node; //节点模型

	// 检查当前操作是否需要认证
	static public function checkAccess() {
		//如果项目要求认证 则进行权限认证
		if( C('USER_AUTH_ON') ){
			$_module	=	array();
			$_action	=	array();
			//无需认证的模块
			$_module['no'] = explode(',',strtoupper(C('NOT_AUTH_MODULE')));
			//检查当前模块是否需要认证
			if((!empty($_module['no']) && !in_array(strtoupper(MODULE_NAME),$_module['no']))) {
				//无需认证的操作
				$_action['no'] = explode(',',strtoupper(C('NOT_AUTH_ACTION')));
				//检查当前操作是否需要认证
				if((!empty($_action['no']) && !in_array(strtoupper(ACTION_NAME),$_action['no']))) {
					return true;
				}else {
					return false;
				}
			}else {
				return false;
			}
		}
		return false;
	}

	//用于检测用户权限的方法,并保存到Session中
	static public function saveAccessList($authId=null) {
		if(null===$authId)   $authId = $_SESSION[C('USER_AUTH_KEY')];
		$_SESSION['_ACCESS_LIST'] = RBAC::getAccessList($authId);
		return ;
	}

	// 取得当前认证号的所有权限列表
	static public function getAccessList($authId=null){
		if(null===$authId)   $authId = $_SESSION[C('USER_AUTH_KEY')];

		$pre = C('DB_PREFIX');
		$t = array('role'=>$pre.C('RBAC_ROLE_TABLE'),'access'=>$pre.C('RBAC_ACCESS_TABLE'),'node'=>$pre.C('RBAC_NODE_TABLE'));
		$access = array();
		$M_node = D('AdminNode');

		// 根据认证号UID得到全部待处理的权限ID
		$sql = "SELECT * FROM $t[access] WHERE uid='$authId' LIMIT 1";
		$rows = $M_node->query($sql);
		if (empty($rows))	 return false;
		$user = array_shift($rows);

		// 基本权限
		if ( $user['nodeids'] ) {
			$where = ' WHERE id IN('.$user['nodeids'].')';
			$sql = "SELECT id,reid,code FROM $t[node] $where AND is_check=1";
			$result = $M_node->query($sql);			
			foreach ($result as $key=>$row){
				$access['node'][$row['id']] = $row;
				$access['nodeids'] .= $row['id'].',';
			}
		}

		// 角色权限
		if ( $user['roleids'] ) {
			$where = ' WHERE id IN('.$user['roleids'].')';
			$sql = "SELECT id,nodeids FROM $t[role] $where AND is_check=1";
			$result = $M_node->query($sql);
			foreach ($result as $row){
				$access['role'][$row['id']] = $row;
				$access['nodeids'] .= $row['nodeids'].',';
			}
		}

		// 权限合并去重复
		$accessAll = array_flip(array_flip( explode(',',trim($access['nodeids'],',')) ));
		sort($accessAll);
		return $accessAll;
	}

	// 权限认证的过滤器方法
	static public function AccessDecision() {
		// 检查是否需要认证
		if(RBAC::checkAccess()) {
			//存在认证识别号，则进行进一步的访问决策
			if( !$_SESSION[C('ADMIN_AUTH_KEY')] ) {
				// 待检查的权限顺序 应用->模块->操作
				$appcode = defined('GROUP_NAME') ? GROUP_NAME : APP_NAME;
				$accessName[0] = $appcode;
				$accessName[1] = $appcode.'_'.MODULE_NAME; // 模块
				$accessName[2] = $appcode.'_'.MODULE_NAME.'_'.ACTION_NAME; // 操作
				$accessName = array_map('strtolower', $accessName);

				if(C('USER_AUTH_TYPE')==2) {
					//加强验证和即时验证模式 更加安全 后台权限修改可以即时生效 通过数据库进行访问检查
					$accessList = RBAC::getAccessList($_SESSION[C('USER_AUTH_KEY')]);
				}else{
					// 如果当前操作已经认证过，无需再次认证					
					foreach ($accessName as $code){
						$accessGuid = md5($code);
						if( isset($_SESSION['_ACCESS_OK'][$accessGuid]) && $_SESSION['_ACCESS_OK'][$accessGuid] == true ) {
							return true;
						}
					}					
					// 登录验证模式，比较登录后保存的权限访问列表
					$accessList = $_SESSION['_ACCESS_LIST'];
				}

				// 开始检查权限
				$M_node = D('AdminNode');
				foreach ($accessName as $code){
					$id = $M_node->code2id($code);					
					$accessGuid = md5($code);
					if (in_array($id,$accessList)) {
						// 有权限操作 直接返回
						$_SESSION['_ACCESS_OK'][$accessGuid] = true;
						return true;
					}
				}
				unset($M_node);
				// 全部检查完毕 没有权限操作 退出
				return false;
			}else{
				//管理员无需认证
				return true;
			}
		}else{
			// 当前操作不需要认证
			return true;
		}
	}

	// 清除权限缓存
	static public function delAccessCache(){
		D('AdminNode')->delCache();
	}
}
?>
